#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>

#include "mail.h"
#include "trace.h"

//#define USE_CERTIFICATE
#if defined(USE_CERTIFICATE)
# define PATH_TO_CERTIFICATE "/path/to/certificate.pem"
#endif

static const char * message_header[] = {
	"Date: Mon, 29 Nov 2010 21:54:29 +1100\n",
	"To: " TO "\n",
	"From: " FROM "\n",
	"Cc: " CC "\n",
	"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd@rfcpedant.example.org>\n",
	"Subject: log2mail report\n",
	"\n", /* empty line to divide headers from body, see RFC5322 */
	NULL
};

static const char ** message_body = NULL;

static char * server   = NULL;
static char * username = NULL;
static char * password = NULL;

struct upload_status
{
	size_t header_lines_read;
	size_t body_lines_read;
};

static size_t payload_source(void * ptr, size_t size, size_t nmemb, void * userp)
{
	//TRACE_DBG("<%s>\n", __FUNCTION__);

	struct upload_status * upload_ctx = (struct upload_status *)userp;
	if (NULL == ptr || NULL == userp)
	{
		TRACE_ERR("<%s> incoming ptr is NULL\n", __FUNCTION__);
		return 0;
	}

	if ((size == 0) || (nmemb == 0) || ((size * nmemb) < 1))
	{
		return 0;
	}

	const char * header = message_header[upload_ctx->header_lines_read];
	const char * body = NULL == message_body ? NULL : message_body[upload_ctx->body_lines_read];
	if (NULL != header)
	{
		size_t len = strlen(header);
		memcpy(ptr, header, len);
		upload_ctx->header_lines_read++;
		return len;
	}
	else if (NULL != body)
	{
		size_t len = strlen(body);
		memcpy(ptr, body, len);
		upload_ctx->body_lines_read++;
		return len;
	}
	return 0;
}

void mail_server_set(const char * str)
{
	TRACE_DBG("<%s>\n", __FUNCTION__);

	if (NULL == str)
	{
		TRACE_ERR("<%s> incoming ptr is NULL\n", __FUNCTION__);
		return;
	}

	if (NULL != server)
		free(server);
	server = strdup(str);
}

void mail_username_set(const char * str)
{
	TRACE_DBG("<%s>\n", __FUNCTION__);

	if (NULL == str)
	{
		TRACE_ERR("<%s> incoming ptr is NULL\n", __FUNCTION__);
		return;
	}

	if (NULL != username)
		free(username);
	username = strdup(str);
}

void mail_password_set(const char * str)
{
	TRACE_DBG("<%s>\n", __FUNCTION__);

	if (NULL == str)
	{
		TRACE_ERR("<%s> incoming ptr is NULL\n", __FUNCTION__);
		return;
	}

	if (NULL != password)
		free(password);
	password = strdup(str);
}

int mail_is_configured()
{
	TRACE_DBG("<%s>\n", __FUNCTION__);

	if (NULL == server || NULL == username || NULL == password)
	{
		return 0;
	}
	return 1;
}

int mail_send(const char ** body)
{
	TRACE_DBG("<%s>\n", __FUNCTION__);

	if (NULL == body)
	{
		TRACE_ERR("<%s> incoming ptr is NULL\n", __FUNCTION__);
		return 1;
	}

	message_body = body;

	CURL * curl = curl_easy_init();
	if (NULL == curl)
	{
		TRACE_ERR("<%s> curl_easy_init failed\n", __FUNCTION__);
		return 1;
	}

	/* This is the URL for your mailserver. Note the use of port 587 here,
	 * instead of the normal SMTP port (25). Port 587 is commonly used for
	 * secure mail submission (see RFC4403), but you should use whatever
	 * matches your server configuration. */
	curl_easy_setopt(curl, CURLOPT_URL, server);

	/* In this example, we'll start with a plain text connection, and upgrade
	 * to Transport Layer Security (TLS) using the STARTTLS command. Be careful
	 * of using CURLUSESSL_TRY here, because if TLS upgrade fails, the transfer
	 * will continue anyway - see the security discussion in the libcurl
	 * tutorial for more details. */
	curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);

	/* If your server doesn't have a valid certificate, then you can disable
	 * part of the Transport Layer Security protection by setting the
	 * CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST options to 0 (false).
	 *   curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
	 *   curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
	 * That is, in general, a bad idea. It is still better than sending your
	 * authentication details in plain text though.
	 * Instead, you should get the issuer certificate (or the host certificate
	 * if the certificate is self-signed) and add it to the set of certificates
	 * that are known to libcurl using CURLOPT_CAINFO and/or CURLOPT_CAPATH. See
	 * docs/SSLCERTS for more information.
	 */
#if defined(USE_CERTIFICATE)
	curl_easy_setopt(curl, CURLOPT_CAINFO, PATH_TO_CERTIFICATE);
#else
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
#endif

	/* A common reason for requiring transport security is to protect
	 * authentication details (user names and passwords) from being "snooped"
	 * on the network. Here is how the user name and password are provided: */
	curl_easy_setopt(curl, CURLOPT_USERNAME, username);
	curl_easy_setopt(curl, CURLOPT_PASSWORD, password);

	/* value for envelope reverse-path */
	curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM);
	/* Add two recipients, in this particular case they correspond to the
	 * To: and Cc: addressees in the header, but they could be any kind of
	 * recipient. */
	struct curl_slist * recipients = NULL;
	recipients = curl_slist_append(recipients, TO);
	recipients = curl_slist_append(recipients, CC);
	curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);

	/* In this case, we're using a callback function to specify the data. You
	 * could just use the CURLOPT_READDATA option to specify a FILE pointer to
	 * read from.
	 */
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
	struct upload_status upload_ctx;
	upload_ctx.header_lines_read = 0;
	upload_ctx.body_lines_read = 0;
	curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);

	/* Since the traffic will be encrypted, it is very useful to turn on debug
	 * information within libcurl to see what is happening during the transfer.
	 */
#if defined(TRACE_DEBUGS)
	curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
#endif

	/* send the message (including headers) */
	CURLcode res = curl_easy_perform(curl);
	/* Check for errors */
	if (CURLE_OK != res)
	{
		TRACE_ERR("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
	}

	/* free the list of recipients and clean up */
	curl_slist_free_all(recipients);
	curl_easy_cleanup(curl);

	message_body = NULL;
	return CURLE_OK == res ? 0 : 1;
}

